import SpeakSocket from "@/utils/speakSocket";
import VoiceSocket from "./voice/voiceSocket";

class Tool {
  public controller: AbortController = new AbortController();
  public speakController: AbortController = new AbortController();
  public audioBufferSource: AudioBufferSourceNode | null = null;
  public speakSocket: SpeakSocket | null = null;
  public speakEndedFn: (() => void) | null = null;
  public speakDataBuffer: ArrayBuffer[] = [];
  public audioContext: AudioContext = new window.AudioContext();
  public voiceSocket: VoiceSocket | null = null;
  public chatRecorder: any | null = null;

  public speaking: boolean = false;

  constructor() {
    this.speaking = false;
  }

  public newController() {
    this.controller = new AbortController();
  }

  public onStopChat() {
    this.controller.abort();
  }

  public onStreamSpeak() {
    const res = this.speakDataBuffer[0];
    if (res && !this.speaking) {
      this.speaking = true;
      if (this.audioBufferSource) this.audioBufferSource.stop();
      this.audioContext.decodeAudioData(res).then(
        (audioBuffer) => {
          this.audioBufferSource = this.audioContext.createBufferSource();
          this.audioBufferSource.buffer = audioBuffer;
          this.audioBufferSource.connect(this.audioContext.destination);
          this.audioBufferSource.start(0);
          this.audioBufferSource.addEventListener("ended", () => {
            this.speaking = false;
            this.speakDataBuffer.shift();
            this.onStreamSpeak();
          });
        },
        (error: any) => {
          this.speaking = false;
          this.speakDataBuffer.shift();
          this.onStreamSpeak();
        }
      );
    } else {
      if (!this.speaking && this.speakSocket === null) {
        this.speakEndedFn && this.speakEndedFn();
        this.speakEndedFn = null;
      }
    }
  }

  public speakPaseMessage(e: any) {
    if (e.data instanceof ArrayBuffer) {
      this.speakDataBuffer.push(e.data);
      this.onStreamSpeak();
    } else if (typeof e.data === "string") {
      const info = JSON.parse(e.data);
      if (info.status && info.status === "finished") {
        if (this.speakSocket) {
          this.speakSocket.close();
          this.speakSocket = null;
        }
        this.onStreamSpeak();
      }
    }
  }

  public onAiStreamSpeak(
    str: string,
    effect?: {
      onEnded?: () => void;
    }
  ) {
    return new Promise((resolve, reject) => {
      if (window.AudioContext) {
        if (this.speaking) this.speaking = false;
        this.speakSocket = new SpeakSocket();
        this.speakSocket.start(str).then(
          () => {
            if (this.speakSocket?.speechSocket) {
              this.speakSocket.speechSocket.onmessage =
                this.speakPaseMessage.bind(this);
            }
            this.speakEndedFn = effect?.onEnded || null;
            resolve(true);
          },
          (err) => {
            this.speakSocket = null;
            this.speakEndedFn = null;
            this.speaking = false;
            reject(err);
          }
        );
      } else {
        reject(new Error("当前环境不支持语音播放"));
      }
    });
  }

  public startRecoderVoice(para?: { onMessage?: (text: string) => void }) {
    return new Promise((resolve, reject) => {
      if (window.HZRecorder.canRecording) {
        this.voiceSocket = new VoiceSocket();
        this.voiceSocket.start({
          onMessage: para?.onMessage
        }).then((socket) => {
          window.HZRecorder.throwError = (message) => {
            this.voiceSocket = null
            this.chatRecorder = null
            reject(new Error(message))
          }
          window.HZRecorder.get((rec) => {
            this.chatRecorder = rec
            this.chatRecorder.start()
            resolve(true)
          }, {
            ws: socket
          })
        }, (err) => {
          this.voiceSocket = null
          this.chatRecorder = null
          reject(err)
        })
      } else {
        this.voiceSocket = null
        this.chatRecorder = null
        reject(new Error("当前浏览器不支持录音功能!"))
      }
    });
  }

  public endRecorderVoice() {
    return new Promise((resolve, reject) => {
      if (this.chatRecorder) {
        this.chatRecorder.stop()
        this.chatRecorder = null
      }
      if (this.voiceSocket) {
        this.voiceSocket.close()
        this.voiceSocket = null
      }
      resolve(true)
    });
  }

  public onStopChatSpeak() {
    this.speakController.abort();
    if (this.audioBufferSource) this.audioBufferSource?.stop();
    if (this.speakSocket) {
      this.speakSocket.close();
      this.speakSocket = null;
    }
    this.speakEndedFn = null;
    this.speakDataBuffer = [];
  }

  public onChatSpeak(
    str: string,
    effect?: {
      onEnded?: () => void;
    }
  ) {
    return this.onAiStreamSpeak(str, effect);
  }

  public startChatVoice(para?: { onMessage?: (text: string) => void }) {
    this.onStopChatSpeak();
    return this.startRecoderVoice(para);
  }

  public endChatVoice(effect?: { onMessage?: (str: string) => void }) {
    return this.endRecorderVoice();
  }
}

const ChatTool = new Tool();

export default ChatTool;
